/*
 * shell.c
 *
 *  Created on: 2016年11月23日
 *      Author: chenshisheng
 */

#include "shell.h"
#include "message_queue.h"
#include "stream/chprintf.h"
#include "stream/io_stream.h"
#include "utils.h"
#include "serial.h"
//#include "power_manager.h"
//#include "ctimer.h"
//#include "version.h"
#include <ctype.h>
#include <mstdio.h>
#include <string.h>
#include <stdlib.h>

// 命令列表
static const Shell_Cmd_t *_cmdList;

// 命令列表长度
static volatile unsigned int _listSize;

#if 0
static Ctimer_t _watchTimer;
static struct
{
    int argc;
    char *argv[16];
    char buf[64];
} _watchCmd;
#endif

#if 1
static struct
{
    Serial_t forward;
    Serial_t backward;
    Serial_RxByteCallback_t forwardOldCb;
    Serial_RxByteCallback_t backwardOldCb;
} _bridgeCtrl;

static struct
{
    uint8_t len;
}_exitBridgeCtrl;
#endif

struct _SerialMsg
{
    Serial_t serial;
    uint8_t b;
};

//static bool _LineSignal(Serial_t serial, uint8_t b);

/**
 * @brief 命令解释器初始化
 * @param cmdList 命令数组
 * @param size    命令数组长度
 */
void Shell_Init(void)
{
    extern unsigned int _shell_cmds_start, _shell_cmds_end; // 来自链接脚本
    const Shell_Cmd_t *start, *end;

    start = (const Shell_Cmd_t *) & _shell_cmds_start;
    end = (const Shell_Cmd_t *) & _shell_cmds_end;
    _cmdList = start;
    _listSize = end - start;

    printf("\nshell>");
}

#if 0
/**
 * @brief 判断目前接收到的字符是否为回车符，若是则
 *        发送 MsgQueue_Id_ShellLineIn 消息以表示接收到一行字符串
 * @param serial 串口序号
 * @param ptr 接收到字符指针
 */
static bool _LineSignal(Serial_t serial, uint8_t b)
{
    (void) serial;
    char c;
    Stream_t *stream;

//    Serial_SendByte(serial, b);
    c = (char) b;
    if((c == '\n'))
    {
//        Serial_SendByte(serial, '\n');
        stream = Mstdio_Stream();
        MsgQueue_Send(MsgQueue_Id_ShellLineIn, & stream, sizeof(stream));
    }

    return TRUE;
}
#endif

/**
 * @brief 以空白字符（\r \t \n \v等）分割字符串，分割出的子串作为argv数组的元素
 * @param argcMax 可分割出的子串的最大数目，即argv数组的长度
 * @param str 待分割的字符串
 * @param argv 容纳分割出来的子串的数组
 * @return  分割出的子串的数目
 */
static int _CmdStringSplit(int argcMax, char *str, char *argv[])
{
    int argc;
    bool argStarted;

    argStarted = FALSE;
    for(argc = 0; argc < argcMax; str++)
    {
        if(*str == '\0')
        {
            if(argStarted)
            {
                argc += 1;
            }

            break;
        }

        if(isspace(*str))
        {
            if(argStarted)
            {
                *str = '\0';
                argc += 1;
                argStarted = FALSE; // 开始获取下一arg
            }
        }
        else
        {
            if(! argStarted)
            {
                argv[argc] = str;
                argStarted = TRUE;
            }
        }
    }

    return argc;
}

static int _Exec(void *stream, int argc, char *argv[])
{
    int ret = 1;
    unsigned int i;
    bool cmdExist;

    if(argc > 0)
    {
        cmdExist = FALSE;
        for(i = 0; i < _listSize; i++)
        {
            if(strcmp(argv[0], _cmdList[i].name) == 0)
            {
                cmdExist = TRUE;
                ret = _cmdList[i].callback(stream, argc, argv);
                break;
            }
        }

        if(! cmdExist)
        {
            chprintf(stream, "Unknown command: %s\n", argv[0]);
        }
    }

    return ret;
}

/**
 * @brief 试图从一行字符串中解释出命令名，并执行该命令
 * @param msg 从消息队列传过来的消息
 */
void Shell_OnLine(const MsgQueue_Msg_t *msg)
{
    char buf[64];
    int argc;
    char *argv[16];
    void *stream;

    memcpy(&stream, msg->data, sizeof(stream));
    if((Stream_ReadUntil(stream, buf, sizeof(buf), '\n') != 0) && (strlen(buf) == 0))
    {
        chprintf(stream, "Shell: Input Error!\n");
        return;
    }

    argc = _CmdStringSplit(ARRAY_LEN(argv), buf, argv);
    _Exec(stream, argc, argv);

    chprintf(stream, "\nshell> ");
    Stream_Flush(stream);
}

void Shell_PrintHelp(void *stream, const char *cmdName)
{
    unsigned int i;
    bool cmdExist = FALSE;

    for(i = 0; i < _listSize; i++)
    {
        if(strcmp(cmdName, _cmdList[i].name) == 0)
        {
            chprintf(stream, "%s\n\nUsage: %s %s\n", _cmdList[i].brief, cmdName, _cmdList[i].usage);
            cmdExist = TRUE;
            break;
        }
    }

    if(! cmdExist)
    {
        chprintf(stream, "Unknown command: %s\n", cmdName);
    }
}

/**
 * @brief 测试命令，打印参数
 * @param argc 命令参数数目
 * @param argv 命令参数数组
 * @return
 */
static int _CmdTest(void *stream, int argc, char *argv[])
{
    int i;

    chprintf(stream, "Shell Test:\n");
    for(i = 0; i < argc; i++)
    {
        chprintf(stream, "arg %d: %s\n", i, argv[i]);
    }

    return 0;
}

SHELL_CMD(test, _CmdTest, "For shell test.", "[<arg 1>] [<arg 2>]...[<arg n>]");

/**
 * @brief 帮助命令，打印支持的命令名
 * @param argc 命令参数数目
 * @param argv 命令参数数组
 * @return
 */
static int _CmdHelp(void *stream, int argc, char *argv[])
{
    unsigned int i;

    if(argc == 1)
    {
        for(i = 0; i < _listSize; i++)
        {
            chprintf(stream, "%s\t - %s\n", _cmdList[i].name, _cmdList[i].brief);
        }
    }
    else if(argc >= 2)
    {
        Shell_PrintHelp(stream, argv[1]);
    }

    return 0;
}

SHELL_CMD(help, _CmdHelp, "Show shell command help message.", "[<command name>]");

#if 0
static void _OnWatchTimer(int data)
{
    UNUSED(data);

    _Exec(_watchCmd.argc, _watchCmd.argv);
}

int Shell_Watch(void *stream, int argc, char *argv[])
{
    int sec, i;
    char *buf;

    if(argc == 1)
    {
        Ctimer_Stop(&_watchTimer);
        chprintf(stream, "Watch: stop\r\n");
    }
    else if(argc < 3)
    {
        Shell_PrintHelp(argv[0]);
    }
    else
    {
        sec = atoi(argv[1]);
        if(sec <= 0)
        {
            Shell_PrintHelp(argv[0]);
        }
        else if(_Exec(argc - 2, & argv[2]) == 0)
        {
            _watchCmd.argc = argc - 2;
            buf = _watchCmd.buf;
            for(i = 0; i < (argc - 2); i++)
            {
                strcpy(buf, argv[i + 2]);
                _watchCmd.argv[i] = buf;
                buf += strlen(argv[i + 2]) + 1;
            }

            Ctimer_Start(&_watchTimer, sec * 1000, _OnWatchTimer, 0, 0);
        }
    }

    return 0;
}
#endif

#if 1
static bool _OnForwardRxByte(Serial_t serial, uint8_t b)
{
    UNUSED(serial);
//    struct _SerialMsg msg;

    if(b == 'q')
    {
        _exitBridgeCtrl.len ++;
        if(_exitBridgeCtrl.len >= 10)
        {
            Serial_SetCallbacks(_bridgeCtrl.backward, NULL, NULL, _bridgeCtrl.backwardOldCb);
            Serial_SetCallbacks(_bridgeCtrl.forward,  NULL, NULL, _bridgeCtrl.forwardOldCb);
            Serial_ClearRxBuffer(_bridgeCtrl.backward);
            Serial_ClearRxBuffer(_bridgeCtrl.forward);
            Serial_SetFlag(STDIO_SERIAL, SERIAL_FLAG_AUTO_LINE);
            printf("Exit Bridge\n");
        }
    }
    else
    {
        _exitBridgeCtrl.len = 0;
    }

    Serial_SendByte(_bridgeCtrl.backward, b);
//    msg.serial = _bridgeCtrl.backward;
//    msg.b = b;
//    MsgQueue_Send(MsgQueue_Id_SerialSend, &msg, sizeof(msg));
    return FALSE;
}

static bool _OnBackwardRxByte(Serial_t serial, uint8_t b)
{
    UNUSED(serial);
//    struct _SerialMsg msg;

    Serial_SendByte(_bridgeCtrl.forward, b);
//    msg.serial = _bridgeCtrl.forward;
//    msg.b = b;
//    MsgQueue_Send(MsgQueue_Id_SerialSend, &msg, sizeof(msg));
    return FALSE;
}

static int _CmdBridge(void *stream, int argc, char *argv[])
{
    Serial_t backward, s;

    if(argc == 1)
    {
        Shell_PrintHelp(stream, argv[0]);
        for(s = 0; s <= Serial_Max; s++)
        {
            chprintf(stream, "serial index: %d -> %s\n", (int)s, Serial_GetName(s));
        }

        return -1;
    }

    backward = (Serial_t) atoi(argv[1]);
    if((backward > Serial_Max) || (backward == STDIO_SERIAL))
    {
        chprintf(stream, "Input Error!\n");
        return -2;
    }

    chprintf(stream, "Build bridge form stdio to %s\n", Serial_GetName(backward));

    memset(& _exitBridgeCtrl, 0, sizeof(_exitBridgeCtrl));

    _bridgeCtrl.backward = backward;
    _bridgeCtrl.forward  = STDIO_SERIAL;
    _exitBridgeCtrl.len = 0;
    Serial_ClrFlag(STDIO_SERIAL, SERIAL_FLAG_AUTO_LINE);
    Serial_GetCallbacks(_bridgeCtrl.backward, NULL, NULL, & _bridgeCtrl.backwardOldCb);
    Serial_GetCallbacks(_bridgeCtrl.forward,  NULL, NULL, & _bridgeCtrl.forwardOldCb);
    Serial_SetCallbacks(_bridgeCtrl.backward, NULL, NULL, _OnBackwardRxByte);
    Serial_SetCallbacks(_bridgeCtrl.forward,  NULL, NULL, _OnForwardRxByte);

    return 0;
}

SHELL_CMD(bridge, _CmdBridge, "Build bridge from stdio to other serial", "<serial index>");

void Shell_OnSerialSend(const MsgQueue_Msg_t *msg)
{
    const struct _SerialMsg *serMsg;

    serMsg = (const struct _SerialMsg *)msg->data;
    Serial_SendByte(serMsg->serial, serMsg->b);
}

#endif

#if 0
int Shell_Version(int argc, char *argv[])
{
    UNUSED(argc);
    UNUSED(argv);

    chprintf(stream, "Version: %s %s\n", PROJECT, VERSION_LONG);
    chprintf(stream, "Build:   %s\n", BUILD_DATE);
    chprintf(stream, "Builder: %s\n", BUILDER);

    return 0;
}
#endif
